#! /usr/bin/env python
# coding=utf-8

import urllib, urllib2, string, time, hashlib, hmac, re, types, logging, sys, threading, socket, httplib, base64, StringIO, exceptions, traceback, datetime
from urllib2 import URLError
import json
import xml
from xml.dom.minidom import parseString
if '..' not in sys.path:
    sys.path.append('..')
import settings
from dateUtil import *
from urlUtil import *

urlTimeout = 120
socket.setdefaulttimeout(urlTimeout)

class TaobaoException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None

class TaobaoParseFormatException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None

class SubscribeException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None

class AppCallLimitedException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None

class TopInvalidSessionException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None
        
class TopInvalidMethodException(exceptions.Exception):
    def __init__(self, *param):
        exceptions.Exception.__init__(self)
        if param:
            self.msg = param[0]

    def __str__(self):
        if self.msg:
            return self.msg
        return None

class DumpRedirector(urllib2.HTTPRedirectHandler):
    def http_error_301(self, req, fp, code, msg, headers):
        return  urllib2.HTTPError(req,"200","OK",headers,fp)

    def http_error_302(self, req, fp, code, msg, headers):
        return  urllib2.HTTPError(req,"200","OK",headers,fp)

class XmlHelper:
    def getText(self, nodeElem):
        rc = []
        for node in nodeElem.childNodes:
            if node.nodeType == node.TEXT_NODE:
                rc.append(node.data)
        return ''.join(rc)

class TaobaoInterface:
    def _getText(self, nodeElem):
        return XmlHelper().getText(nodeElem)

    #签名函数
    def _sign(self, param):
        src = settings.TAOBAO_APP_SECRET + ''.join(["%s%s" % (k, v) for k, v in sorted(param.items())]) + settings.TAOBAO_APP_SECRET
        return hashlib.md5(src).hexdigest().upper()

    def _getTaobaoDataTemplate(self, params):
        result = self.readURL(params)
        return self.parseRsp(result['paramDict'], result['rsp'])
    
    def readURL(self, params):
        t = time.localtime()
        paramDict = {
            'app_key': settings.TAOBAO_APP_KEY,
            'format': 'json',
            'v': '2.0',
            'timestamp': time.strftime('%Y-%m-%d %X', t),
            'sign_method': 'md5'
            }
        
        for k, v in params.items():
            paramDict[k] = v

        # some param may be unicode format, especially chinese
        paramDict = convert2Utf8(paramDict)

        settings.logger.debug(paramDict)

        #生成签名
        sign = self._sign(paramDict);
        paramDict['sign'] = sign

        #组装参数
        form_data = urllib.urlencode(paramDict)

        #访问服务
        try:
            urlopen = urllib2.urlopen(settings.TAOBAO_ENV_URL, form_data)
        except URLError:
            settings.logger.error('\nvisit taobao interface error begin')
            settings.logger.error(traceback.format_exc())
            settings.logger.error('visit taobao interface error end\n')
            raise TaobaoException('taobao param validate exception')

        rsp = urlopen.read();
        urlopen.close()
        if paramDict['format'] == 'json':
            rsp = rsp.decode('UTF-8');

        settings.logger.debug(rsp)
        
        return convert2Unicode({
            'paramDict': paramDict,
            'rsp': rsp
        })
    
    def parseRsp(self, paramDict, rsp, isLogger=True):
        # 进行结果的整理,对json的解析
        try:
            if paramDict['format'] == 'json':
                rspStruct = json.loads(rsp, strict=False)
            else:
                #在task data中,从数据库获取数据,默认是unicode,需要encode utf8,而直接返回中,不需要encode,
                rsp = re.sub('&#\w+;', '', rsp)
                if type(rsp).__name__ == 'unicode':
                    rspStruct = parseString(rsp.encode('utf8'))
                else:
                    rspStruct = parseString(rsp)
        except ValueError:
            if isLogger:
                settings.logger.error('\ntaobao return value json analytics error begin')
                settings.logger.error(paramDict)
                settings.logger.error(rsp)
                settings.logger.error(traceback.format_exc())
                settings.logger.error('taobao return value json analytics error end\n')
            raise TaobaoParseFormatException('taobao data template return value json format exception')
        except xml.parsers.expat.ExpatError:
            if isLogger:
                settings.logger.error('\ntaobao return value xml analytics error begin')
                settings.logger.error(paramDict)
                settings.logger.error(rsp)
                settings.logger.error(traceback.format_exc())
                settings.logger.error('taobao return value xml analytics error end\n')
            raise TaobaoParseFormatException('taobao data template return value xml format exception')

        # 错误暂时直接返回空,要记录日志
        if paramDict['format'] == 'json':
            if rspStruct.get('error_response'):
                if isLogger:
                    settings.logger.error('\ntaobao json return error_response begin')
                    settings.logger.error(paramDict)
                    settings.logger.error(rsp)
                    settings.logger.error('taobao json return error_response end\n')
                if rspStruct['error_response']['code'] == settings.TOP_INVALID_SESSION_CODE:
                    raise TopInvalidSessionException('Invalid app Key')
                if rspStruct['error_response']['code'] == 7:
                    raise AppCallLimitedException('app call limited exception')
                if rspStruct['error_response']['code'] == 22:
                    raise TopInvalidMethodException('Invalid method')
                raise TaobaoException('taobao data template return error response')
        else:
            if rspStruct.getElementsByTagName('error_response'):
                if isLogger:
                    settings.logger.error('\ntaobao xml return error_response begin')
                    settings.logger.error(paramDict)
                    settings.logger.error(rsp)
                    settings.logger.error('taobao xml return error_response end\n')
                errorResponse = rspStruct.getElementsByTagName('error_response')[0]
                if self._getText(errorResponse.getElementsByTagName('code')[0]) == str(settings.TOP_INVALID_SESSION_CODE):
                    raise TopInvalidSessionException('Invalid app Key')
                if self._getText(errorResponse.getElementsByTagName('code')[0]) == '7':
                    raise AppCallLimitedException('app call limited exception')
                raise TaobaoException('taobao data template return error response')

        return rspStruct

    def _validateResponse(self, resStruct):
        topAppKey = resStruct['top_appkey']
        topParam = resStruct['top_parameters']
        topSession = resStruct['top_session']
        topSign = resStruct['top_sign']
        if topAppKey != settings.TAOBAO_APP_KEY:
            return False

        md5Digest = hashlib.md5(topAppKey + topParam + topSession + settings.TAOBAO_APP_SECRET).digest()
        if base64.b64encode(md5Digest) != topSign:
            return False

        return True

    def _decodeTopParameter(self, topParameters):
        param = base64.b64decode(topParameters)
        paramLi = param.split('&')
        paramLi = [item.split('=') for item in paramLi]
        result = {}
        for item in paramLi:
            result[item[0]] = urllib.unquote(item[1])
        return convert2Unicode(result)

    def _validateTimestamp(self, ts):
        dotTs = '%s.%s' % (ts[:-3], ts[-3:])
        taobaoTs = datetime.datetime.fromtimestamp(float(dotTs))
        current = datetime.datetime.now()
        begin = current + datetime.timedelta(minutes=-5)
        end = current + datetime.timedelta(minutes=5)
        if not (begin <= taobaoTs and taobaoTs <= end):
            return False
        return True

    def _validateUserSubscribe(self, resStruct):
        param = {
            'method': 'taobao.appstore.subscribe.get',
            'nick': resStruct['topParameter']['visitor_nick']
            }

        result = self._getTaobaoDataTemplate(param)
        userSubscribe = result['appstore_subscribe_get_response']['user_subscribe']
        validateStatus = userSubscribe['status'] in ['subscribeUser', 'unsubscribeUser']
        if validateStatus:
            if userSubscribe['status'] == 'unsubscribeUser':
                return False

            startDate = getYMDHMSFromString(userSubscribe['start_date'])
            endDate = getYMDHMSFromString(userSubscribe['end_date'])
            current = datetime.datetime.now()
            validateDate = startDate <= current and current <= endDate
            if validateDate:
                return True
        return False

    def _getCallbackDict(self, callbackUrl):
        #http://localhost/?top_appkey={appkey}&top_parameters=xxx&top_session=xxx&top_sign=xxx
        callbackDict = {}
        li = callbackUrl.split('?')
        callbackDict['url'] = li[0]

        paramLi = li[1].split('&')
        paramLi = [item.split('=') for item in paramLi]
        
        for item in paramLi:
            callbackDict[item[0]] = urllib.unquote(item[1])

        callbackDict['topParameter'] = self._decodeTopParameter(callbackDict['top_parameters'])

        return callbackDict

    def _getUserInfo(self, url):
        result = self._getCallbackDict(url)
        # validate response
        if not self._validateResponse(result):
            raise TaobaoException('taobao param validate exception')
        # validate ts
        if not self._validateTimestamp(result['topParameter']['ts']):
            raise TaobaoException('taobao timestamp validate exception')
        # validate user subscribe
#        if settings.VALIDATE_SUBSCRIBE and not self._validateUserSubscribe(result):
#            raise SubscribeException('user not subscribe or time out')
        return result

    def getUserInfo(self, url):
        try:
            return self._getUserInfo(url)
        except URLError:
            settings.logger.error('\nvalidate user info from taobao cause error begin')
            settings.logger.error(url)
            settings.logger.error(traceback.format_exc())
            settings.logger.error('validate user info from taobao cause error end\n')
            raise TaobaoException('call taobao interface exception')

    def taobaoShopGet(self, taobaoSysDict):
        param = {
            'method': 'taobao.shop.get',
            'session': taobaoSysDict['top_session'],
            'fields': 'sid,cid,title,nick',
            'nick': taobaoSysDict['topParameter']['visitor_nick']
            }
        return self._getTaobaoDataTemplate(param)

    def wangwangEserviceGroupMemberGet(self, resStruct):
        param = {
            'method': 'taobao.wangwang.eservice.groupmember.get',
            'session': resStruct['top_session'],
            'manager_id': resStruct['topParameter']['visitor_nick']
            }
#        return self._getTaobaoDataTemplate(param)
        result = self._getTaobaoDataTemplate(param)
#        result = {"groupMemberList":[{"groupId":"185419","memberList":"cntaobao艾姆斯,cntaobao艾姆斯:1,cntaobao艾姆斯:2","groupName":"客服组"}]}
#{"groupMemberList":[{"groupId":"213135","memberList":"cntaobao阿宝老板,cntaobao阿宝老板:导购5,cntaobao阿宝老板:导购3,cntaobao阿宝老板:导购1,cntaobao阿宝老板:导购4,cntaobao阿宝老板:导购2,cntaobao阿宝老板:导购6","groupName":"售前"}]}
        try:
            settings.logger.error('group member get raParam=' + str(param))
            settings.logger.error('result=' + str(result))
            groupMember = result['wangwang_eservice_groupmember_get_response']['group_member_list']['GroupMember']
        except exceptions.Exception:
#{"groupMemberList":[{"groupId":"185419","memberList":"cntaobao艾姆斯,cntaobao艾姆斯:1,cntaobao艾姆斯:2","groupName":"客服组"}]}
#{
#02	    "wangwang_eservice_groupmember_get_response": {
#03	        "result_code": 0,
#04	        "group_member_list": {
#05	            "GroupMember": [{
#06	                "group_name": "group1",
#07	                "member_list": "cntaobaohello,cntaobaoabc"
#08	            }]
#09	        }
#10	    }
            try:
                if len(result['groupMemberList']) == 0:
                    raise TaobaoException('group member list is null')
                return {
                    u"wangwang_eservice_groupmember_get_response": {
                        u"result_code": 0,
                        u"group_member_list": {
                            u"GroupMember": [convert2Unicode({
                                    u"group_name": item['groupName'],
                                    u"member_list": item['memberList'].replace('cntaobao', '')
                                    }) for item in result['groupMemberList']]
                            }
                        }
                    }
            except exceptions.Exception:
                raise TaobaoException('group member return error')
        return result

    #查询子账号列表
    def sellercenterSubusersGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.sellercenter.subusers.get',
            'session': resStruct['top_session']
            }
        for k,v in appParam.items():
            param[k] = v
        return self.readURL(param)

    # appParam:result(good,neutral,bad),page_no,page_size
    def traderatesGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.traderates.get',
            'session': resStruct['top_session'],
            'fields': 'tid,result,content,nick,item_title,created',
            'rate_type': 'get',
            'role': 'buyer'
        }
        for k,v in appParam.items():
            param[k] = v
        return self.readURL(param)
        #return self._getTaobaoDataTemplate(param)
    
    # taobao.trades.sold.get 搜索当前会话用户作为卖家已卖出的交易数据,TODO,所需的字段,
    def tradesSoldGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.trades.sold.get',
            'session': resStruct['top_session'],
            'fields': 'tid,seller_nick,buyer_nick,title,status,payment,num,created',
            'status': 'TRADE_FINISHED'
        }
        for k,v in appParam.items():
            param[k] = v
        return self.readURL(param)
        #return self._getTaobaoDataTemplate(param)

    # taobao.wangwang.eservice.chatpeers.get 获取聊天对象列表
    # 目前只返回xml,返回的内容与接口不一致
    # <?xml version="1.0" encoding="UTF-8"?>
    # <chatpeers_get_response>
    # <ret>10000</ret>
    # <count>5</count>
    # <chatpeers list="true">
    # <chatpeer>
    # <uid>cntaobaobltbsrwg11204927</uid>
    # <date>2010-09-20</date></chatpeer>
    # <chatpeer>
    # <uid>cntaobaotop技术支持</uid>
    # <date>2010-09-20</date></chatpeer>
    # </chatpeers>
    # </chatpeers_get_response>
    def wangwangEserviceChatpeersGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.chatpeers.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }
        for k,v in appParam.items():
            param[k] = v

        xmlStruct= self._getTaobaoDataTemplate(param)
        rspStruct = {}
        rspStruct['chatpeers_get_response'] = {}
        rspStruct['chatpeers_get_response']['ret'] = int(self._getText(xmlStruct.getElementsByTagName('ret')[0]))
        rspStruct['chatpeers_get_response']['count'] = int(self._getText(xmlStruct.getElementsByTagName('count')[0]))
        rspStruct['chatpeers_get_response']['chatpeers'] = {}
        rspStruct['chatpeers_get_response']['chatpeers']['chatpeer'] = []
        for node in xmlStruct.getElementsByTagName('chatpeer'):
            chatPeer = {
                'uid': self._getText(node.getElementsByTagName('uid')[0]),
                'date': self._getText(node.getElementsByTagName('date')[0])
            }
            rspStruct['chatpeers_get_response']['chatpeers']['chatpeer'].append(chatPeer)

        
        return rspStruct

    # taobao.wangwang.eservice.chatlog.get 获取具体的聊天记录
    def wangwangEserviceChatlogGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.chatlog.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        xmlStruct= self._getTaobaoDataTemplate(param)
        rspStruct = {}
        rspStruct['chatlog_get_response'] = {}
        rspStruct['chatlog_get_response']['ret'] = int(self._getText(xmlStruct.getElementsByTagName('ret')[0]))
        rspStruct['chatlog_get_response']['count'] = int(self._getText(xmlStruct.getElementsByTagName('count')[0]))
        rspStruct['chatlog_get_response']['msgs'] = {}
        rspStruct['chatlog_get_response']['msgs']['msg'] = []
        for node in xmlStruct.getElementsByTagName('msg'):
            msg = {
                'direction': int(self._getText(node.getElementsByTagName('direction')[0])),
                'time': self._getText(node.getElementsByTagName('time')[0]),
                'content': self._getText(node.getElementsByTagName('content')[0])
            }
            rspStruct['chatlog_get_response']['msgs']['msg'].append(msg)

        
        return rspStruct

    # taobao.wangwang.eservice.chatrecord.get 聊天记录查询
    def wangwangEserviceChatlogGetData(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.chatlog.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)
        
    # taobao.wangwang.eservice.chatrecord.get 聊天记录查询
    def wangwangEserviceChatrecordGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.chatrecord.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)
        
    # taobao.wangwang.eservice.avgwaittime.get 平均等待时长
    def wangwangEserviceAvgwaittimeGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.avgwaittime.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }
        
        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)
        
    # taobao.wangwang.eservice.evaluation.get 客服评价统计
    def wangwangEserviceEvaluationGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.evaluation.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)

    # taobao.wangwang.eservice.noreplynum.get 客服未回复人数
    def wangwangEserviceNoreplynumGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.noreplynum.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)

    # taobao.wangwang.eservice.onlinetime.get 日累计在线时长
    def wangwangEserviceOnlinetimeGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.onlinetime.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)

    # taobao.wangwang.eservice.receivenum.get 客服接待数
    def wangwangEserviceReceivenumGet(self, resStruct, appParam):
        param = {
            'method': 'taobao.wangwang.eservice.receivenum.get',
            'format': 'xml',
            'session': resStruct['top_session']
        }

        for k,v in appParam.items():
            param[k] = v

        return self.readURL(param)

